]>
Commit | Line | Data |
---|---|---|
f2459fe7 JG |
1 | /* |
2 | * Copyright (c) 2010 Nicira Networks. | |
3 | * Distributed under the terms of the GNU GPL version 2. | |
4 | * | |
5 | * Significant portions of this file may be copied from parts of the Linux | |
6 | * kernel, by Linus Torvalds and others. | |
7 | */ | |
8 | ||
9 | #include <linux/if_arp.h> | |
10 | #include <linux/if_bridge.h> | |
11 | #include <linux/if_vlan.h> | |
12 | #include <linux/kernel.h> | |
13 | #include <linux/llc.h> | |
14 | #include <linux/rtnetlink.h> | |
15 | #include <linux/skbuff.h> | |
16 | ||
17 | #include <net/llc.h> | |
18 | ||
19 | #include "datapath.h" | |
20 | #include "vport-internal_dev.h" | |
21 | #include "vport-netdev.h" | |
22 | ||
23 | #include "compat.h" | |
24 | ||
b4d76c80 JG |
25 | /* If the native device stats aren't 64 bit use the vport stats tracking instead. */ |
26 | #define USE_VPORT_STATS (sizeof(((struct net_device_stats *)0)->rx_bytes) < sizeof(u64)) | |
27 | ||
43158509 | 28 | static void netdev_port_receive(struct vport *vport, struct sk_buff *skb); |
f2459fe7 | 29 | |
43158509 SH |
30 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) |
31 | /* Called with rcu_read_lock and bottom-halves disabled. */ | |
32 | static struct sk_buff *netdev_frame_hook(struct sk_buff *skb) | |
33 | { | |
34 | struct vport *vport; | |
35 | ||
36 | if (unlikely(skb->pkt_type == PACKET_LOOPBACK)) | |
37 | return skb; | |
38 | ||
37f055c7 | 39 | vport = netdev_get_vport(skb->dev); |
43158509 SH |
40 | |
41 | netdev_port_receive(vport, skb); | |
42 | ||
43 | return NULL; | |
44 | } | |
45 | #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) | |
f2459fe7 JG |
46 | /* |
47 | * Used as br_handle_frame_hook. (Cannot run bridge at the same time, even on | |
48 | * different set of devices!) | |
49 | */ | |
f2459fe7 | 50 | /* Called with rcu_read_lock and bottom-halves disabled. */ |
fceb2a5b JG |
51 | static struct sk_buff *netdev_frame_hook(struct net_bridge_port *p, |
52 | struct sk_buff *skb) | |
f2459fe7 | 53 | { |
43158509 | 54 | netdev_port_receive((struct vport *)p, skb); |
f2459fe7 JG |
55 | return NULL; |
56 | } | |
57 | #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) | |
43158509 SH |
58 | /* |
59 | * Used as br_handle_frame_hook. (Cannot run bridge at the same time, even on | |
60 | * different set of devices!) | |
61 | */ | |
f2459fe7 | 62 | /* Called with rcu_read_lock and bottom-halves disabled. */ |
fceb2a5b | 63 | static int netdev_frame_hook(struct net_bridge_port *p, struct sk_buff **pskb) |
f2459fe7 | 64 | { |
43158509 | 65 | netdev_port_receive((struct vport *)p, *pskb); |
f2459fe7 JG |
66 | return 1; |
67 | } | |
68 | #else | |
69 | #error | |
70 | #endif | |
71 | ||
43158509 SH |
72 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) |
73 | static int netdev_init(void) { return 0; } | |
74 | static void netdev_exit(void) { } | |
75 | #else | |
fceb2a5b | 76 | static int netdev_init(void) |
f2459fe7 | 77 | { |
f2459fe7 JG |
78 | /* Hook into callback used by the bridge to intercept packets. |
79 | * Parasites we are. */ | |
80 | br_handle_frame_hook = netdev_frame_hook; | |
81 | ||
82 | return 0; | |
83 | } | |
84 | ||
fbb29a54 | 85 | static void netdev_exit(void) |
f2459fe7 JG |
86 | { |
87 | br_handle_frame_hook = NULL; | |
f2459fe7 | 88 | } |
43158509 | 89 | #endif |
f2459fe7 | 90 | |
fceb2a5b | 91 | static struct vport *netdev_create(const char *name, const void __user *config) |
f2459fe7 JG |
92 | { |
93 | struct vport *vport; | |
94 | struct netdev_vport *netdev_vport; | |
95 | int err; | |
96 | ||
97 | vport = vport_alloc(sizeof(struct netdev_vport), &netdev_vport_ops); | |
98 | if (IS_ERR(vport)) { | |
99 | err = PTR_ERR(vport); | |
100 | goto error; | |
101 | } | |
102 | ||
103 | netdev_vport = netdev_vport_priv(vport); | |
104 | ||
105 | netdev_vport->dev = dev_get_by_name(&init_net, name); | |
106 | if (!netdev_vport->dev) { | |
107 | err = -ENODEV; | |
108 | goto error_free_vport; | |
109 | } | |
110 | ||
111 | if (netdev_vport->dev->flags & IFF_LOOPBACK || | |
112 | netdev_vport->dev->type != ARPHRD_ETHER || | |
113 | is_internal_dev(netdev_vport->dev)) { | |
114 | err = -EINVAL; | |
115 | goto error_put; | |
116 | } | |
117 | ||
b4d76c80 JG |
118 | /* If we are using the vport stats layer initialize it to the current |
119 | * values so we are roughly consistent with the device stats. */ | |
120 | if (USE_VPORT_STATS) { | |
ec61a01c | 121 | struct rtnl_link_stats64 stats; |
b4d76c80 JG |
122 | |
123 | err = netdev_get_stats(vport, &stats); | |
124 | if (!err) | |
125 | vport_set_stats(vport, &stats); | |
126 | } | |
127 | ||
f2459fe7 JG |
128 | return vport; |
129 | ||
130 | error_put: | |
131 | dev_put(netdev_vport->dev); | |
132 | error_free_vport: | |
133 | vport_free(vport); | |
134 | error: | |
135 | return ERR_PTR(err); | |
136 | } | |
137 | ||
fceb2a5b | 138 | static int netdev_destroy(struct vport *vport) |
f2459fe7 JG |
139 | { |
140 | struct netdev_vport *netdev_vport = netdev_vport_priv(vport); | |
141 | ||
142 | dev_put(netdev_vport->dev); | |
143 | vport_free(vport); | |
144 | ||
145 | return 0; | |
146 | } | |
147 | ||
fceb2a5b | 148 | static int netdev_attach(struct vport *vport) |
f2459fe7 JG |
149 | { |
150 | struct netdev_vport *netdev_vport = netdev_vport_priv(vport); | |
43158509 SH |
151 | int err; |
152 | ||
43158509 | 153 | err = netdev_rx_handler_register(netdev_vport->dev, netdev_frame_hook, |
37f055c7 | 154 | vport); |
43158509 SH |
155 | if (err) |
156 | return err; | |
f2459fe7 JG |
157 | |
158 | dev_set_promiscuity(netdev_vport->dev, 1); | |
159 | dev_disable_lro(netdev_vport->dev); | |
37f055c7 | 160 | netdev_vport->dev->priv_flags |= IFF_OVS_DATAPATH; |
f2459fe7 JG |
161 | |
162 | return 0; | |
163 | } | |
164 | ||
fceb2a5b | 165 | static int netdev_detach(struct vport *vport) |
f2459fe7 JG |
166 | { |
167 | struct netdev_vport *netdev_vport = netdev_vport_priv(vport); | |
168 | ||
37f055c7 | 169 | netdev_vport->dev->priv_flags &= ~IFF_OVS_DATAPATH; |
43158509 | 170 | netdev_rx_handler_unregister(netdev_vport->dev); |
f2459fe7 JG |
171 | dev_set_promiscuity(netdev_vport->dev, -1); |
172 | ||
173 | return 0; | |
174 | } | |
175 | ||
fceb2a5b | 176 | int netdev_set_mtu(struct vport *vport, int mtu) |
f2459fe7 JG |
177 | { |
178 | struct netdev_vport *netdev_vport = netdev_vport_priv(vport); | |
179 | return dev_set_mtu(netdev_vport->dev, mtu); | |
180 | } | |
181 | ||
fceb2a5b | 182 | int netdev_set_addr(struct vport *vport, const unsigned char *addr) |
f2459fe7 JG |
183 | { |
184 | struct netdev_vport *netdev_vport = netdev_vport_priv(vport); | |
185 | struct sockaddr sa; | |
186 | ||
187 | sa.sa_family = ARPHRD_ETHER; | |
188 | memcpy(sa.sa_data, addr, ETH_ALEN); | |
189 | ||
190 | return dev_set_mac_address(netdev_vport->dev, &sa); | |
191 | } | |
192 | ||
fceb2a5b | 193 | const char *netdev_get_name(const struct vport *vport) |
f2459fe7 JG |
194 | { |
195 | const struct netdev_vport *netdev_vport = netdev_vport_priv(vport); | |
196 | return netdev_vport->dev->name; | |
197 | } | |
198 | ||
fceb2a5b | 199 | const unsigned char *netdev_get_addr(const struct vport *vport) |
f2459fe7 JG |
200 | { |
201 | const struct netdev_vport *netdev_vport = netdev_vport_priv(vport); | |
202 | return netdev_vport->dev->dev_addr; | |
203 | } | |
204 | ||
fceb2a5b | 205 | struct kobject *netdev_get_kobj(const struct vport *vport) |
f2459fe7 JG |
206 | { |
207 | const struct netdev_vport *netdev_vport = netdev_vport_priv(vport); | |
208 | return &netdev_vport->dev->NETDEV_DEV_MEMBER.kobj; | |
209 | } | |
210 | ||
ec61a01c | 211 | int netdev_get_stats(const struct vport *vport, struct rtnl_link_stats64 *stats) |
f2459fe7 JG |
212 | { |
213 | const struct netdev_vport *netdev_vport = netdev_vport_priv(vport); | |
ec61a01c | 214 | dev_get_stats(netdev_vport->dev, stats); |
f2459fe7 JG |
215 | return 0; |
216 | } | |
217 | ||
fceb2a5b | 218 | unsigned netdev_get_dev_flags(const struct vport *vport) |
f2459fe7 JG |
219 | { |
220 | const struct netdev_vport *netdev_vport = netdev_vport_priv(vport); | |
221 | return dev_get_flags(netdev_vport->dev); | |
222 | } | |
223 | ||
fceb2a5b | 224 | int netdev_is_running(const struct vport *vport) |
f2459fe7 JG |
225 | { |
226 | const struct netdev_vport *netdev_vport = netdev_vport_priv(vport); | |
227 | return netif_running(netdev_vport->dev); | |
228 | } | |
229 | ||
fceb2a5b | 230 | unsigned char netdev_get_operstate(const struct vport *vport) |
f2459fe7 JG |
231 | { |
232 | const struct netdev_vport *netdev_vport = netdev_vport_priv(vport); | |
233 | return netdev_vport->dev->operstate; | |
234 | } | |
235 | ||
fbb29a54 | 236 | int netdev_get_ifindex(const struct vport *vport) |
f2459fe7 JG |
237 | { |
238 | const struct netdev_vport *netdev_vport = netdev_vport_priv(vport); | |
239 | return netdev_vport->dev->ifindex; | |
240 | } | |
241 | ||
fceb2a5b | 242 | int netdev_get_iflink(const struct vport *vport) |
f2459fe7 JG |
243 | { |
244 | const struct netdev_vport *netdev_vport = netdev_vport_priv(vport); | |
245 | return netdev_vport->dev->iflink; | |
246 | } | |
247 | ||
fceb2a5b | 248 | int netdev_get_mtu(const struct vport *vport) |
f2459fe7 JG |
249 | { |
250 | const struct netdev_vport *netdev_vport = netdev_vport_priv(vport); | |
251 | return netdev_vport->dev->mtu; | |
252 | } | |
253 | ||
254 | /* Must be called with rcu_read_lock. */ | |
43158509 | 255 | static void netdev_port_receive(struct vport *vport, struct sk_buff *skb) |
f2459fe7 | 256 | { |
f2459fe7 JG |
257 | /* Make our own copy of the packet. Otherwise we will mangle the |
258 | * packet for anyone who came before us (e.g. tcpdump via AF_PACKET). | |
259 | * (No one comes after us, since we tell handle_bridge() that we took | |
260 | * the packet.) */ | |
261 | skb = skb_share_check(skb, GFP_ATOMIC); | |
67c74f75 | 262 | if (unlikely(!skb)) |
f2459fe7 JG |
263 | return; |
264 | ||
67c74f75 JG |
265 | skb_warn_if_lro(skb); |
266 | ||
f2459fe7 | 267 | skb_push(skb, ETH_HLEN); |
f4267e34 | 268 | compute_ip_summed(skb, false); |
f2459fe7 JG |
269 | |
270 | vport_receive(vport, skb); | |
271 | } | |
272 | ||
fceb2a5b | 273 | static int netdev_send(struct vport *vport, struct sk_buff *skb) |
f2459fe7 JG |
274 | { |
275 | struct netdev_vport *netdev_vport = netdev_vport_priv(vport); | |
276 | int len = skb->len; | |
277 | ||
278 | skb->dev = netdev_vport->dev; | |
279 | forward_ip_summed(skb); | |
280 | dev_queue_xmit(skb); | |
281 | ||
282 | return len; | |
283 | } | |
284 | ||
285 | /* Returns null if this device is not attached to a datapath. */ | |
fceb2a5b | 286 | struct vport *netdev_get_vport(struct net_device *dev) |
f2459fe7 | 287 | { |
37f055c7 SH |
288 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) |
289 | /* XXX: The bridge code may have registered the data. | |
290 | * So check that the handler pointer is the datapath's. | |
291 | * Once the merge is done and IFF_OVS_DATAPATH stops | |
292 | * being the same value as IFF_BRIDGE_PORT the check can | |
293 | * simply be netdev_vport->dev->priv_flags & IFF_OVS_DATAPATH. */ | |
294 | if (rcu_dereference(dev->rx_handler) != netdev_frame_hook) | |
295 | return NULL; | |
296 | return (struct vport *)rcu_dereference(dev->rx_handler_data); | |
297 | #else | |
f59a10a8 | 298 | return (struct vport *)rcu_dereference(dev->br_port); |
37f055c7 | 299 | #endif |
f2459fe7 JG |
300 | } |
301 | ||
302 | struct vport_ops netdev_vport_ops = { | |
303 | .type = "netdev", | |
b4d76c80 JG |
304 | .flags = (VPORT_F_REQUIRED | |
305 | (USE_VPORT_STATS ? VPORT_F_GEN_STATS : 0)), | |
f2459fe7 JG |
306 | .init = netdev_init, |
307 | .exit = netdev_exit, | |
308 | .create = netdev_create, | |
309 | .destroy = netdev_destroy, | |
310 | .attach = netdev_attach, | |
311 | .detach = netdev_detach, | |
312 | .set_mtu = netdev_set_mtu, | |
313 | .set_addr = netdev_set_addr, | |
314 | .get_name = netdev_get_name, | |
315 | .get_addr = netdev_get_addr, | |
316 | .get_kobj = netdev_get_kobj, | |
317 | .get_stats = netdev_get_stats, | |
318 | .get_dev_flags = netdev_get_dev_flags, | |
319 | .is_running = netdev_is_running, | |
320 | .get_operstate = netdev_get_operstate, | |
321 | .get_ifindex = netdev_get_ifindex, | |
322 | .get_iflink = netdev_get_iflink, | |
323 | .get_mtu = netdev_get_mtu, | |
324 | .send = netdev_send, | |
325 | }; | |
106eab5d | 326 | |
37f055c7 | 327 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) |
106eab5d | 328 | /* |
37f055c7 SH |
329 | * In kernels earlier than 2.6.36, Open vSwitch cannot safely coexist with |
330 | * the Linux bridge module on any released version of Linux, because there | |
331 | * is only a single bridge hook function and only a single br_port member | |
332 | * in struct net_device. | |
106eab5d BP |
333 | * |
334 | * Declaring and exporting this symbol enforces mutual exclusion. The bridge | |
335 | * module also exports the same symbol, so the module loader will refuse to | |
336 | * load both modules at the same time (e.g. "bridge: exports duplicate symbol | |
337 | * br_should_route_hook (owned by openvswitch_mod)"). | |
338 | * | |
339 | * The use of "typeof" here avoids the need to track changes in the type of | |
340 | * br_should_route_hook over various kernel versions. | |
341 | */ | |
342 | typeof(br_should_route_hook) br_should_route_hook; | |
343 | EXPORT_SYMBOL(br_should_route_hook); | |
37f055c7 | 344 | #endif |