26d8dd91a46d6d3023faeb8aedf2b33cae049b05
[sliver-openvswitch.git] / datapath / linux-2.6 / compat-2.6 / veth.c
1 /* veth driver port to Linux 2.6.18 */
2
3 /*
4  *  drivers/net/veth.c
5  *
6  *  Copyright (C) 2007, 2009 OpenVZ http://openvz.org, SWsoft Inc
7  *
8  * Author: Pavel Emelianov <xemul@openvz.org>
9  * Ethtool interface from: Eric W. Biederman <ebiederm@xmission.com>
10  *
11  */
12
13 #include <linux/list.h>
14 #include <linux/netdevice.h>
15 #include <linux/ethtool.h>
16 #include <linux/etherdevice.h>
17
18 #include <net/dst.h>
19 #include <net/xfrm.h>
20
21 #define DRV_NAME        "veth"
22 #define DRV_VERSION     "1.0"
23
24 struct veth_net_stats {
25         unsigned long   rx_packets;
26         unsigned long   tx_packets;
27         unsigned long   rx_bytes;
28         unsigned long   tx_bytes;
29         unsigned long   tx_dropped;
30 };
31
32 struct veth_priv {
33         struct net_device *peer;
34         struct net_device *dev;
35         struct list_head list;
36         struct veth_net_stats *stats;
37         unsigned ip_summed;
38         struct net_device_stats dev_stats;
39 };
40
41 static LIST_HEAD(veth_list);
42
43 /*
44  * ethtool interface
45  */
46
47 static struct {
48         const char string[ETH_GSTRING_LEN];
49 } ethtool_stats_keys[] = {
50         { "peer_ifindex" },
51 };
52
53 static int veth_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
54 {
55         cmd->supported          = 0;
56         cmd->advertising        = 0;
57         cmd->speed              = SPEED_10000;
58         cmd->duplex             = DUPLEX_FULL;
59         cmd->port               = PORT_TP;
60         cmd->phy_address        = 0;
61         cmd->transceiver        = XCVR_INTERNAL;
62         cmd->autoneg            = AUTONEG_DISABLE;
63         cmd->maxtxpkt           = 0;
64         cmd->maxrxpkt           = 0;
65         return 0;
66 }
67
68 static void veth_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
69 {
70         strcpy(info->driver, DRV_NAME);
71         strcpy(info->version, DRV_VERSION);
72         strcpy(info->fw_version, "N/A");
73 }
74
75 static void veth_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
76 {
77         switch(stringset) {
78         case ETH_SS_STATS:
79                 memcpy(buf, &ethtool_stats_keys, sizeof(ethtool_stats_keys));
80                 break;
81         }
82 }
83
84 static void veth_get_ethtool_stats(struct net_device *dev,
85                 struct ethtool_stats *stats, u64 *data)
86 {
87         struct veth_priv *priv;
88
89         priv = netdev_priv(dev);
90         data[0] = priv->peer->ifindex;
91 }
92
93 static u32 veth_get_rx_csum(struct net_device *dev)
94 {
95         struct veth_priv *priv;
96
97         priv = netdev_priv(dev);
98         return priv->ip_summed == CHECKSUM_UNNECESSARY;
99 }
100
101 static int veth_set_rx_csum(struct net_device *dev, u32 data)
102 {
103         struct veth_priv *priv;
104
105         priv = netdev_priv(dev);
106         priv->ip_summed = data ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE;
107         return 0;
108 }
109
110 static u32 veth_get_tx_csum(struct net_device *dev)
111 {
112         return (dev->features & NETIF_F_NO_CSUM) != 0;
113 }
114
115 static int veth_set_tx_csum(struct net_device *dev, u32 data)
116 {
117         if (data)
118                 dev->features |= NETIF_F_NO_CSUM;
119         else
120                 dev->features &= ~NETIF_F_NO_CSUM;
121         return 0;
122 }
123
124 static struct ethtool_ops veth_ethtool_ops = {
125         .get_settings           = veth_get_settings,
126         .get_drvinfo            = veth_get_drvinfo,
127         .get_link               = ethtool_op_get_link,
128         .get_rx_csum            = veth_get_rx_csum,
129         .set_rx_csum            = veth_set_rx_csum,
130         .get_tx_csum            = veth_get_tx_csum,
131         .set_tx_csum            = veth_set_tx_csum,
132         .get_sg                 = ethtool_op_get_sg,
133         .set_sg                 = ethtool_op_set_sg,
134         .get_strings            = veth_get_strings,
135         .get_ethtool_stats      = veth_get_ethtool_stats,
136 };
137
138 /*
139  * xmit
140  */
141
142 static int veth_xmit(struct sk_buff *skb, struct net_device *dev)
143 {
144         struct net_device *rcv = NULL;
145         struct veth_priv *priv, *rcv_priv;
146         struct veth_net_stats *stats;
147         int length, cpu;
148
149         skb_orphan(skb);
150
151         priv = netdev_priv(dev);
152         rcv = priv->peer;
153         rcv_priv = netdev_priv(rcv);
154
155         cpu = smp_processor_id();
156         stats = per_cpu_ptr(priv->stats, cpu);
157
158         if (!(rcv->flags & IFF_UP))
159                 goto outf;
160
161         skb->dev = rcv;
162         skb->pkt_type = PACKET_HOST;
163         skb->protocol = eth_type_trans(skb, rcv);
164         if (dev->features & NETIF_F_NO_CSUM)
165                 skb->ip_summed = rcv_priv->ip_summed;
166
167         dst_release(skb->dst);
168         skb->dst = NULL;
169         secpath_reset(skb);
170         nf_reset(skb);
171
172         length = skb->len;
173
174         stats->tx_bytes += length;
175         stats->tx_packets++;
176
177         stats = per_cpu_ptr(rcv_priv->stats, cpu);
178         stats->rx_bytes += length;
179         stats->rx_packets++;
180
181         netif_rx(skb);
182         return 0;
183
184 outf:
185         kfree_skb(skb);
186         stats->tx_dropped++;
187         return 0;
188 }
189
190 /*
191  * general routines
192  */
193
194 static struct net_device_stats *veth_get_stats(struct net_device *dev)
195 {
196         struct veth_priv *priv;
197         struct net_device_stats *dev_stats;
198         int cpu;
199         struct veth_net_stats *stats;
200
201         priv = netdev_priv(dev);
202         dev_stats = &priv->dev_stats;
203
204         dev_stats->rx_packets = 0;
205         dev_stats->tx_packets = 0;
206         dev_stats->rx_bytes = 0;
207         dev_stats->tx_bytes = 0;
208         dev_stats->tx_dropped = 0;
209
210         for_each_online_cpu(cpu) {
211                 stats = per_cpu_ptr(priv->stats, cpu);
212
213                 dev_stats->rx_packets += stats->rx_packets;
214                 dev_stats->tx_packets += stats->tx_packets;
215                 dev_stats->rx_bytes += stats->rx_bytes;
216                 dev_stats->tx_bytes += stats->tx_bytes;
217                 dev_stats->tx_dropped += stats->tx_dropped;
218         }
219
220         return dev_stats;
221 }
222
223 static int veth_open(struct net_device *dev)
224 {
225         struct veth_priv *priv;
226
227         priv = netdev_priv(dev);
228         if (priv->peer == NULL)
229                 return -ENOTCONN;
230
231         if (priv->peer->flags & IFF_UP) {
232                 netif_carrier_on(dev);
233                 netif_carrier_on(priv->peer);
234         }
235         return 0;
236 }
237
238 static int veth_dev_init(struct net_device *dev)
239 {
240         struct veth_net_stats *stats;
241         struct veth_priv *priv;
242
243         stats = alloc_percpu(struct veth_net_stats);
244         if (stats == NULL)
245                 return -ENOMEM;
246
247         priv = netdev_priv(dev);
248         priv->stats = stats;
249         return 0;
250 }
251
252 static void veth_dev_free(struct net_device *dev)
253 {
254         struct veth_priv *priv;
255
256         priv = netdev_priv(dev);
257         free_percpu(priv->stats);
258         free_netdev(dev);
259 }
260
261 static void veth_setup(struct net_device *dev)
262 {
263         ether_setup(dev);
264
265         dev->hard_start_xmit = veth_xmit;
266         dev->get_stats = veth_get_stats;
267         dev->open = veth_open;
268         dev->ethtool_ops = &veth_ethtool_ops;
269         dev->features |= NETIF_F_LLTX;
270         dev->init = veth_dev_init;
271         dev->destructor = veth_dev_free;
272 }
273
274 static void veth_change_state(struct net_device *dev)
275 {
276         struct net_device *peer;
277         struct veth_priv *priv;
278
279         priv = netdev_priv(dev);
280         peer = priv->peer;
281
282         if (netif_carrier_ok(peer)) {
283                 if (!netif_carrier_ok(dev))
284                         netif_carrier_on(dev);
285         } else {
286                 if (netif_carrier_ok(dev))
287                         netif_carrier_off(dev);
288         }
289 }
290
291 static int veth_device_event(struct notifier_block *unused,
292                              unsigned long event, void *ptr)
293 {
294         struct net_device *dev = ptr;
295
296         if (dev->open != veth_open)
297                 goto out;
298
299         switch (event) {
300         case NETDEV_CHANGE:
301                 veth_change_state(dev);
302                 break;
303         }
304 out:
305         return NOTIFY_DONE;
306 }
307
308 static struct notifier_block veth_notifier_block __read_mostly = {
309         .notifier_call  = veth_device_event,
310 };
311
312 /*
313  * netlink interface
314  */
315
316 static int veth_newlink(const char *devname, const char *peername)
317 {
318         int err;
319         const char *names[2];
320         struct net_device *devs[2];
321         int i;
322
323         names[0] = devname;
324         names[1] = peername;
325         devs[0] = devs[1] = NULL;
326
327         for (i = 0; i < 2; i++) {
328                 struct net_device *dev;
329
330                 err = -ENOMEM;
331                 devs[i] = alloc_netdev(sizeof(struct veth_priv),
332                                        names[i], veth_setup);
333                 if (!devs[i]) {
334                         goto err;
335                 }
336
337                 dev = devs[i];
338
339                 if (strchr(dev->name, '%')) {
340                         err = dev_alloc_name(dev, dev->name);
341                         if (err < 0)
342                                 goto err;
343                 }
344                 random_ether_addr(dev->dev_addr);
345
346                 err = register_netdevice(dev);
347                 if (err < 0)
348                         goto err;
349
350                 netif_carrier_off(dev);
351         }
352
353         /*
354          * tie the devices together
355          */
356
357         for (i = 0; i < 2; i++) {
358                 struct veth_priv *priv = netdev_priv(devs[i]);
359                 priv->dev = devs[i];
360                 priv->peer = devs[!i];
361                 if (!i)
362                         list_add(&priv->list, &veth_list);
363                 else
364                         INIT_LIST_HEAD(&priv->list);
365         }
366         return 0;
367
368 err:
369         for (i = 0; i < 2; i++) {
370                 if (devs[i]) {
371                         if (devs[i]->reg_state != NETREG_UNINITIALIZED)
372                                 unregister_netdevice(devs[i]);
373                         else
374                                 free_netdev(devs[i]);
375                 }
376         }
377         return err;
378 }
379
380 static void veth_dellink(struct net_device *dev)
381 {
382         struct veth_priv *priv;
383         struct net_device *peer;
384
385         priv = netdev_priv(dev);
386         peer = priv->peer;
387
388         if (!list_empty(&priv->list))
389                 list_del(&priv->list);
390
391         priv = netdev_priv(peer);
392         if (!list_empty(&priv->list))
393                 list_del(&priv->list);
394
395         unregister_netdevice(dev);
396         unregister_netdevice(peer);
397 }
398
399 /*
400  * sysfs 
401  */
402
403 /*
404  * "show" function for the veth_pairs attribute.
405  * The class parameter is ignored.
406  */
407 static ssize_t veth_show_veth_pairs(struct class *cls, char *buffer)
408 {
409         int res = 0;
410         struct veth_priv *priv;
411
412         list_for_each_entry(priv, &veth_list, list) {
413                 if (res > (PAGE_SIZE - (IFNAMSIZ * 2 + 1))) {
414                         /* not enough space for another interface name */
415                         if ((PAGE_SIZE - res) > 10)
416                                 res = PAGE_SIZE - 10;
417                         res += sprintf(buffer + res, "++more++");
418                         break;
419                 }
420                 res += sprintf(buffer + res, "%s,%s ",
421                                priv->dev->name, priv->peer->name);
422         }
423         res += sprintf(buffer + res, "\n");
424         res++;
425         return res;
426 }
427
428 /*
429  * "store" function for the veth_pairs attribute.  This is what
430  * creates and deletes veth pairs.
431  *
432  * The class parameter is ignored.
433  *
434  */
435 static ssize_t veth_store_veth_pairs(struct class *cls, const char *buffer,
436                                      size_t count)
437 {
438         int c = *buffer++;
439         int retval;
440         if (c == '+') {
441                 char devname[IFNAMSIZ + 1] = "";
442                 char peername[IFNAMSIZ + 1] = "";
443                 char *comma = strchr(buffer, ',');
444                 if (!comma)
445                         goto err_no_cmd;
446                 strncat(devname, buffer,
447                         min_t(int, sizeof devname, comma - buffer));
448                 strncat(peername, comma + 1,
449                         min_t(int, sizeof peername, strcspn(comma + 1, "\n")));
450                 if (!dev_valid_name(devname) || !dev_valid_name(peername))
451                         goto err_no_cmd;
452                 rtnl_lock();
453                 retval = veth_newlink(devname, peername);
454                 rtnl_unlock();
455                 return retval ? retval : count;
456         } else if (c == '-') {
457                 char devname[IFNAMSIZ + 1] = "";
458                 struct net_device *dev;
459
460                 strncat(devname, buffer,
461                         min_t(int, sizeof devname, strcspn(buffer, "\n")));
462                 rtnl_lock();
463                 dev = __dev_get_by_name(devname);
464                 if (!dev)
465                         retval = -ENODEV;
466                 else if (dev->init != veth_dev_init)
467                         retval = -EINVAL;
468                 else {
469                         veth_dellink(dev);
470                         retval = count;
471                 }
472                 rtnl_unlock();
473
474                 return retval;
475         }
476
477 err_no_cmd:
478         printk(KERN_ERR DRV_NAME ": no command found in veth_pairs.  Use +ifname,peername or -ifname.\n");
479         return -EPERM;
480 }
481
482 /* class attribute for veth_pairs file.  This ends up in /sys/class/net */
483 static CLASS_ATTR(veth_pairs,  S_IWUSR | S_IRUGO,
484                   veth_show_veth_pairs, veth_store_veth_pairs);
485
486 static struct class *netdev_class;
487
488 /*
489  * Initialize sysfs.  This sets up the veth_pairs file in
490  * /sys/class/net.
491  */
492 int veth_create_sysfs(void)
493 {
494         struct net_device *dev = dev_get_by_name("lo");
495         if (!dev)
496                 return -ESRCH;
497         netdev_class = dev->class_dev.class;
498         if (!netdev_class)
499                 return -ENODEV;
500         
501         return class_create_file(netdev_class, &class_attr_veth_pairs);
502 }
503
504 /*
505  * Remove /sys/class/net/veth_pairs.
506  */
507 void veth_destroy_sysfs(void)
508 {
509         class_remove_file(netdev_class, &class_attr_veth_pairs);
510 }
511
512
513
514 /*
515  * init/fini
516  */
517
518 static __init int veth_init(void)
519 {
520         int retval = veth_create_sysfs();
521         if (retval)
522                 return retval;
523         register_netdevice_notifier(&veth_notifier_block);
524         return 0;
525 }
526
527 static __exit void veth_exit(void)
528 {
529         struct veth_priv *p, *n;
530
531         rtnl_lock();
532
533         list_for_each_entry_safe(p, n, &veth_list, list)
534                 veth_dellink(p->dev);
535
536         rtnl_unlock();
537
538         unregister_netdevice_notifier(&veth_notifier_block);
539         veth_destroy_sysfs();
540 }
541
542 module_init(veth_init);
543 module_exit(veth_exit);
544
545 MODULE_DESCRIPTION("Virtual Ethernet Tunnel");
546 MODULE_LICENSE("GPL v2");