/* * linux/drivers/net/vnet.c * * Written by Herbert Pötzl, 25/10/2003 * * Copyright 2003 by Herbert Pötzl. * Redistribution of this file is permitted under the * GNU General Public License. * */ #include #include #include #include /* contains net_device */ #include #include #include #include #include struct vnet_private { struct net_device_stats vnet_stats; struct net_device *vnet_real; }; /* Net device open. */ static int vnet_open(struct net_device *dev) { printk("ˇˇˇ open(%p)\n", dev); netif_start_queue(dev); return 0; } /* Net device stop. */ static int vnet_stop(struct net_device *dev) { printk("ˇˇˇ stop(%p)\n", dev); netif_stop_queue(dev); return 0; } static int vnet_xmit(struct sk_buff *skb, struct net_device *dev) { struct vnet_private *priv = (struct vnet_private *)dev->priv; struct net_device_stats *stats = &priv->vnet_stats; /* * Optimise so buffers with skb->free=1 are not copied but * instead are lobbed from tx queue to rx queue */ printk("ˇˇˇ vnet_xmit(%p,%p)\n", skb, dev); if(atomic_read(&skb->users) != 1) { struct sk_buff *skb2=skb; skb=skb_clone(skb, GFP_ATOMIC); /* Clone the buffer */ if(skb==NULL) { kfree_skb(skb2); return 0; } kfree_skb(skb2); } else skb_orphan(skb); skb->protocol=eth_type_trans(skb, dev); skb->dev=dev; #ifndef LOOPBACK_MUST_CHECKSUM skb->ip_summed = CHECKSUM_UNNECESSARY; #endif dev->last_rx = jiffies; // stats->rx_bytes+=skb->len; stats->tx_bytes+=skb->len; // stats->rx_packets++; stats->tx_packets++; // netif_rx(skb); netif_rx_ni(skb); // netif_receive_skb(skb); return(0); } static struct net_device_stats *vnet_get_stats(struct net_device *dev) { return (struct net_device_stats *)dev->priv; } int vnet_init(struct net_device *dev) { dev->mtu = (16 * 1024) + 20 + 20 + 12; dev->hard_start_xmit = vnet_xmit; // dev->poll = vnet_poll; dev->open = vnet_open; dev->stop = vnet_stop; dev->get_stats = vnet_get_stats; /* dev->hard_header = eth_header; dev->hard_header_cache = eth_header_cache; dev->header_cache_update= eth_header_cache_update; dev->hard_header_len = ETH_HLEN; dev->addr_len = ETH_ALEN; dev->rebuild_header = eth_rebuild_header; */ ether_setup(dev); dev->features = NETIF_F_NO_CSUM; dev->tx_queue_len = 0; dev->weight = 4; // dev->flags |= IFF_NOARP; // dev->flags &= ~IFF_MULTICAST; // dev->type = ARPHRD_ETHER; /* ARPHRD_LOOPBACK; */ /* 0x0001 */ dev->priv = kmalloc(sizeof(struct vnet_private), GFP_KERNEL); if (dev->priv == NULL) return -ENOMEM; memset(dev->priv, 0, sizeof(struct vnet_private)); dev->get_stats = vnet_get_stats; return(0); }; static struct net_device dev_vnet; /* * Check if it's for virtual services, look it up, * and send it on its way... */ static unsigned int vnet_in(unsigned int hooknum, struct sk_buff **skb_p, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { struct sk_buff *skb = *skb_p; struct iphdr *iph = skb->nh.iph; int ret; printk("vnet[%d]: " "packet i[%s]->o[%s] type=%d proto=%d " "saddr=%d.%d.%d.%d daddr=%d.%d.%d.%d\n", hooknum, (in?in->name:NULL), (out?out->name:NULL), skb->pkt_type, iph->protocol, NIPQUAD(iph->saddr), NIPQUAD(iph->daddr)); if (((iph->daddr >> 24) & 0xff) == 10) { struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC); nskb->dev = &dev_vnet; // ret = ip_direct_send(nskb); netif_rx_ni(skb); // dev_vnet.stats.rx_packets++; printk("vnet: redir [%d]\n", ret); // skb->dev = &dev_vnet; } return NF_ACCEPT; } static struct nf_hook_ops vnet_in_ops = { { NULL, NULL }, vnet_in, PF_INET, NF_IP_LOCAL_IN, 100 }; static struct nf_hook_ops vnet_pre_ops = { { NULL, NULL }, vnet_in, PF_INET, NF_IP_PRE_ROUTING, 100 }; static struct nf_hook_ops vnet_forward_ops = { { NULL, NULL }, vnet_in, PF_INET, NF_IP_FORWARD, 100 }; static struct nf_hook_ops vnet_out_ops = { { NULL, NULL }, vnet_in, PF_INET, NF_IP_LOCAL_OUT, 100 }; static struct nf_hook_ops vnet_post_ops = { { NULL, NULL }, vnet_in, PF_INET, NF_IP_POST_ROUTING, 100 }; static int __init vnet_init_module(void) { int err; dev_vnet.init = vnet_init; SET_MODULE_OWNER(&dev_vnet); /* Find a name for this unit */ err=dev_alloc_name(&dev_vnet, "vnet%d"); if(err<0) return err; err = register_netdev(&dev_vnet); if (err<0) return err; /* cleanup? */ err = nf_register_hook(&vnet_in_ops); if (err<0) return err; /* cleanup? */ err = nf_register_hook(&vnet_pre_ops); if (err<0) return err; /* cleanup? */ err = nf_register_hook(&vnet_forward_ops); if (err<0) return err; /* cleanup? */ err = nf_register_hook(&vnet_out_ops); if (err<0) return err; /* cleanup? */ err = nf_register_hook(&vnet_post_ops); if (err<0) return err; /* cleanup? */ netif_start_queue(&dev_vnet); /* enable receive? */ return 0; } static void __exit vnet_cleanup_module(void) { netif_stop_queue(&dev_vnet); /* disable receive? */ unregister_netdev(&dev_vnet); kfree(dev_vnet.priv); memset(&dev_vnet, 0, sizeof(dev_vnet)); dev_vnet.init = vnet_init; } module_init(vnet_init_module); module_exit(vnet_cleanup_module); MODULE_LICENSE("GPL");