aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/veth.c
diff options
context:
space:
mode:
authorPavel Emelyanov <xemul@openvz.org>2007-09-25 19:14:46 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2007-10-10 19:47:46 -0400
commite314dbdc1c0dc6a548ecf0afce28ecfd538ff568 (patch)
treef774e598ebb997c108b1d757cd4b27f7bbca9a28 /drivers/net/veth.c
parente71992889ee289a87f6641cfa40d64a5699bcb53 (diff)
[NET]: Virtual ethernet device driver.
Veth stands for Virtual ETHernet. It is a simple tunnel driver that works at the link layer and looks like a pair of ethernet devices interconnected with each other. Mainly it allows to communicate between network namespaces but it can be used as is as well. The newlink callback is organized that way to make it easy to create the peer device in the separate namespace when we have them in kernel. This implementation uses another interface - the RTM_NRELINK message introduced by Patric. Bug fixes from Daniel Lezcano. Signed-off-by: Pavel Emelyanov <xemul@openvz.org> Acked-by: Patrick McHardy <kaber@trash.net> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/veth.c')
-rw-r--r--drivers/net/veth.c477
1 files changed, 477 insertions, 0 deletions
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
new file mode 100644
index 00000000000..ca1c6893b80
--- /dev/null
+++ b/drivers/net/veth.c
@@ -0,0 +1,477 @@
1/*
2 * drivers/net/veth.c
3 *
4 * Copyright (C) 2007 OpenVZ http://openvz.org, SWsoft Inc
5 *
6 * Author: Pavel Emelianov <xemul@openvz.org>
7 * Ethtool interface from: Eric W. Biederman <ebiederm@xmission.com>
8 *
9 */
10
11#include <linux/list.h>
12#include <linux/netdevice.h>
13#include <linux/ethtool.h>
14#include <linux/etherdevice.h>
15
16#include <net/dst.h>
17#include <net/xfrm.h>
18#include <net/veth.h>
19
20#define DRV_NAME "veth"
21#define DRV_VERSION "1.0"
22
23struct veth_net_stats {
24 unsigned long rx_packets;
25 unsigned long tx_packets;
26 unsigned long rx_bytes;
27 unsigned long tx_bytes;
28 unsigned long tx_dropped;
29};
30
31struct veth_priv {
32 struct net_device *peer;
33 struct net_device *dev;
34 struct list_head list;
35 struct veth_net_stats *stats;
36 unsigned ip_summed;
37};
38
39static LIST_HEAD(veth_list);
40
41/*
42 * ethtool interface
43 */
44
45static struct {
46 const char string[ETH_GSTRING_LEN];
47} ethtool_stats_keys[] = {
48 { "peer_ifindex" },
49};
50
51static int veth_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
52{
53 cmd->supported = 0;
54 cmd->advertising = 0;
55 cmd->speed = SPEED_10000;
56 cmd->duplex = DUPLEX_FULL;
57 cmd->port = PORT_TP;
58 cmd->phy_address = 0;
59 cmd->transceiver = XCVR_INTERNAL;
60 cmd->autoneg = AUTONEG_DISABLE;
61 cmd->maxtxpkt = 0;
62 cmd->maxrxpkt = 0;
63 return 0;
64}
65
66static void veth_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
67{
68 strcpy(info->driver, DRV_NAME);
69 strcpy(info->version, DRV_VERSION);
70 strcpy(info->fw_version, "N/A");
71}
72
73static void veth_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
74{
75 switch(stringset) {
76 case ETH_SS_STATS:
77 memcpy(buf, &ethtool_stats_keys, sizeof(ethtool_stats_keys));
78 break;
79 }
80}
81
82static int veth_get_stats_count(struct net_device *dev)
83{
84 return ARRAY_SIZE(ethtool_stats_keys);
85}
86
87static void veth_get_ethtool_stats(struct net_device *dev,
88 struct ethtool_stats *stats, u64 *data)
89{
90 struct veth_priv *priv;
91
92 priv = netdev_priv(dev);
93 data[0] = priv->peer->ifindex;
94}
95
96static u32 veth_get_rx_csum(struct net_device *dev)
97{
98 struct veth_priv *priv;
99
100 priv = netdev_priv(dev);
101 return priv->ip_summed == CHECKSUM_UNNECESSARY;
102}
103
104static int veth_set_rx_csum(struct net_device *dev, u32 data)
105{
106 struct veth_priv *priv;
107
108 priv = netdev_priv(dev);
109 priv->ip_summed = data ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE;
110 return 0;
111}
112
113static u32 veth_get_tx_csum(struct net_device *dev)
114{
115 return (dev->features & NETIF_F_NO_CSUM) != 0;
116}
117
118static int veth_set_tx_csum(struct net_device *dev, u32 data)
119{
120 if (data)
121 dev->features |= NETIF_F_NO_CSUM;
122 else
123 dev->features &= ~NETIF_F_NO_CSUM;
124 return 0;
125}
126
127static struct ethtool_ops veth_ethtool_ops = {
128 .get_settings = veth_get_settings,
129 .get_drvinfo = veth_get_drvinfo,
130 .get_link = ethtool_op_get_link,
131 .get_rx_csum = veth_get_rx_csum,
132 .set_rx_csum = veth_set_rx_csum,
133 .get_tx_csum = veth_get_tx_csum,
134 .set_tx_csum = veth_set_tx_csum,
135 .get_sg = ethtool_op_get_sg,
136 .set_sg = ethtool_op_set_sg,
137 .get_strings = veth_get_strings,
138 .get_stats_count = veth_get_stats_count,
139 .get_ethtool_stats = veth_get_ethtool_stats,
140};
141
142/*
143 * xmit
144 */
145
146static int veth_xmit(struct sk_buff *skb, struct net_device *dev)
147{
148 struct net_device *rcv = NULL;
149 struct veth_priv *priv, *rcv_priv;
150 struct veth_net_stats *stats;
151 int length, cpu;
152
153 skb_orphan(skb);
154
155 priv = netdev_priv(dev);
156 rcv = priv->peer;
157 rcv_priv = netdev_priv(rcv);
158
159 cpu = smp_processor_id();
160 stats = per_cpu_ptr(priv->stats, cpu);
161
162 if (!(rcv->flags & IFF_UP))
163 goto outf;
164
165 skb->pkt_type = PACKET_HOST;
166 skb->protocol = eth_type_trans(skb, rcv);
167 if (dev->features & NETIF_F_NO_CSUM)
168 skb->ip_summed = rcv_priv->ip_summed;
169
170 dst_release(skb->dst);
171 skb->dst = NULL;
172 skb->mark = 0;
173 secpath_reset(skb);
174 nf_reset(skb);
175
176 length = skb->len;
177
178 stats->tx_bytes += length;
179 stats->tx_packets++;
180
181 stats = per_cpu_ptr(rcv_priv->stats, cpu);
182 stats->rx_bytes += length;
183 stats->rx_packets++;
184
185 netif_rx(skb);
186 return 0;
187
188outf:
189 kfree_skb(skb);
190 stats->tx_dropped++;
191 return 0;
192}
193
194/*
195 * general routines
196 */
197
198static struct net_device_stats *veth_get_stats(struct net_device *dev)
199{
200 struct veth_priv *priv;
201 struct net_device_stats *dev_stats;
202 int cpu;
203 struct veth_net_stats *stats;
204
205 priv = netdev_priv(dev);
206 dev_stats = &dev->stats;
207
208 dev_stats->rx_packets = 0;
209 dev_stats->tx_packets = 0;
210 dev_stats->rx_bytes = 0;
211 dev_stats->tx_bytes = 0;
212 dev_stats->tx_dropped = 0;
213
214 for_each_online_cpu(cpu) {
215 stats = per_cpu_ptr(priv->stats, cpu);
216
217 dev_stats->rx_packets += stats->rx_packets;
218 dev_stats->tx_packets += stats->tx_packets;
219 dev_stats->rx_bytes += stats->rx_bytes;
220 dev_stats->tx_bytes += stats->tx_bytes;
221 dev_stats->tx_dropped += stats->tx_dropped;
222 }
223
224 return dev_stats;
225}
226
227static int veth_open(struct net_device *dev)
228{
229 struct veth_priv *priv;
230
231 priv = netdev_priv(dev);
232 if (priv->peer == NULL)
233 return -ENOTCONN;
234
235 if (priv->peer->flags & IFF_UP) {
236 netif_carrier_on(dev);
237 netif_carrier_on(priv->peer);
238 }
239 return 0;
240}
241
242static int veth_close(struct net_device *dev)
243{
244 struct veth_priv *priv;
245
246 if (netif_carrier_ok(dev)) {
247 priv = netdev_priv(dev);
248 netif_carrier_off(dev);
249 netif_carrier_off(priv->peer);
250 }
251 return 0;
252}
253
254static int veth_dev_init(struct net_device *dev)
255{
256 struct veth_net_stats *stats;
257 struct veth_priv *priv;
258
259 stats = alloc_percpu(struct veth_net_stats);
260 if (stats == NULL)
261 return -ENOMEM;
262
263 priv = netdev_priv(dev);
264 priv->stats = stats;
265 return 0;
266}
267
268static void veth_dev_free(struct net_device *dev)
269{
270 struct veth_priv *priv;
271
272 priv = netdev_priv(dev);
273 free_percpu(priv->stats);
274 free_netdev(dev);
275}
276
277static void veth_setup(struct net_device *dev)
278{
279 ether_setup(dev);
280
281 dev->hard_start_xmit = veth_xmit;
282 dev->get_stats = veth_get_stats;
283 dev->open = veth_open;
284 dev->stop = veth_close;
285 dev->ethtool_ops = &veth_ethtool_ops;
286 dev->features |= NETIF_F_LLTX;
287 dev->init = veth_dev_init;
288 dev->destructor = veth_dev_free;
289}
290
291/*
292 * netlink interface
293 */
294
295static int veth_validate(struct nlattr *tb[], struct nlattr *data[])
296{
297 if (tb[IFLA_ADDRESS]) {
298 if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
299 return -EINVAL;
300 if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
301 return -EADDRNOTAVAIL;
302 }
303 return 0;
304}
305
306static struct rtnl_link_ops veth_link_ops;
307
308static int veth_newlink(struct net_device *dev,
309 struct nlattr *tb[], struct nlattr *data[])
310{
311 int err;
312 struct net_device *peer;
313 struct veth_priv *priv;
314 char ifname[IFNAMSIZ];
315 struct nlattr *peer_tb[IFLA_MAX + 1], **tbp;
316
317 /*
318 * create and register peer first
319 *
320 * struct ifinfomsg is at the head of VETH_INFO_PEER, but we
321 * skip it since no info from it is useful yet
322 */
323
324 if (data != NULL && data[VETH_INFO_PEER] != NULL) {
325 struct nlattr *nla_peer;
326
327 nla_peer = data[VETH_INFO_PEER];
328 err = nla_parse(peer_tb, IFLA_MAX,
329 nla_data(nla_peer) + sizeof(struct ifinfomsg),
330 nla_len(nla_peer) - sizeof(struct ifinfomsg),
331 ifla_policy);
332 if (err < 0)
333 return err;
334
335 err = veth_validate(peer_tb, NULL);
336 if (err < 0)
337 return err;
338
339 tbp = peer_tb;
340 } else
341 tbp = tb;
342
343 if (tbp[IFLA_IFNAME])
344 nla_strlcpy(ifname, tbp[IFLA_IFNAME], IFNAMSIZ);
345 else
346 snprintf(ifname, IFNAMSIZ, DRV_NAME "%%d");
347
348 peer = rtnl_create_link(ifname, &veth_link_ops, tbp);
349 if (IS_ERR(peer))
350 return PTR_ERR(peer);
351
352 if (tbp[IFLA_ADDRESS] == NULL)
353 random_ether_addr(peer->dev_addr);
354
355 err = register_netdevice(peer);
356 if (err < 0)
357 goto err_register_peer;
358
359 netif_carrier_off(peer);
360
361 /*
362 * register dev last
363 *
364 * note, that since we've registered new device the dev's name
365 * should be re-allocated
366 */
367
368 if (tb[IFLA_ADDRESS] == NULL)
369 random_ether_addr(dev->dev_addr);
370
371 if (tb[IFLA_IFNAME])
372 nla_strlcpy(dev->name, tb[IFLA_IFNAME], IFNAMSIZ);
373 else
374 snprintf(dev->name, IFNAMSIZ, DRV_NAME "%%d");
375
376 if (strchr(dev->name, '%')) {
377 err = dev_alloc_name(dev, dev->name);
378 if (err < 0)
379 goto err_alloc_name;
380 }
381
382 err = register_netdevice(dev);
383 if (err < 0)
384 goto err_register_dev;
385
386 netif_carrier_off(dev);
387
388 /*
389 * tie the deviced together
390 */
391
392 priv = netdev_priv(dev);
393 priv->dev = dev;
394 priv->peer = peer;
395 list_add(&priv->list, &veth_list);
396
397 priv = netdev_priv(peer);
398 priv->dev = peer;
399 priv->peer = dev;
400 INIT_LIST_HEAD(&priv->list);
401 return 0;
402
403err_register_dev:
404 /* nothing to do */
405err_alloc_name:
406 unregister_netdevice(peer);
407 return err;
408
409err_register_peer:
410 free_netdev(peer);
411 return err;
412}
413
414static void veth_dellink(struct net_device *dev)
415{
416 struct veth_priv *priv;
417 struct net_device *peer;
418
419 priv = netdev_priv(dev);
420 peer = priv->peer;
421
422 if (!list_empty(&priv->list))
423 list_del(&priv->list);
424
425 priv = netdev_priv(peer);
426 if (!list_empty(&priv->list))
427 list_del(&priv->list);
428
429 unregister_netdevice(dev);
430 unregister_netdevice(peer);
431}
432
433static const struct nla_policy veth_policy[VETH_INFO_MAX + 1];
434
435static struct rtnl_link_ops veth_link_ops = {
436 .kind = DRV_NAME,
437 .priv_size = sizeof(struct veth_priv),
438 .setup = veth_setup,
439 .validate = veth_validate,
440 .newlink = veth_newlink,
441 .dellink = veth_dellink,
442 .policy = veth_policy,
443 .maxtype = VETH_INFO_MAX,
444};
445
446/*
447 * init/fini
448 */
449
450static __init int veth_init(void)
451{
452 return rtnl_link_register(&veth_link_ops);
453}
454
455static __exit void veth_exit(void)
456{
457 struct veth_priv *priv, *next;
458
459 rtnl_lock();
460 /*
461 * cannot trust __rtnl_link_unregister() to unregister all
462 * devices, as each ->dellink call will remove two devices
463 * from the list at once.
464 */
465 list_for_each_entry_safe(priv, next, &veth_list, list)
466 veth_dellink(priv->dev);
467
468 __rtnl_link_unregister(&veth_link_ops);
469 rtnl_unlock();
470}
471
472module_init(veth_init);
473module_exit(veth_exit);
474
475MODULE_DESCRIPTION("Virtual Ethernet Tunnel");
476MODULE_LICENSE("GPL v2");
477MODULE_ALIAS_RTNL_LINK(DRV_NAME);