Olej писал(а): ↑18 июн 2022, 20:47
простой виртуальный сетевой интерфейс
Виртуальный сетевой интерфейс. Делаем и проверяем.
В архиве их 3:
Код: Выделить всё
olej@R420:~/2022/own.BOOKs/BHV.kernel/examples/network/virt$ ls -l *.ko
-rw-rw-r-- 1 olej olej 12328 июн 20 10:58 virt1.ko
-rw-rw-r-- 1 olej olej 12208 июн 20 10:58 virt2.ko
-rw-rw-r-- 1 olej olej 12248 июн 20 10:58 virt.ko
Но отличаются они только тем, как и в каких структурах у них накапливается статистика трафика, 3
старых способа (ndo_get_stats не ndo_get_stats64), как и было описано в книге.
Выглядит это примерно так:
Код: Выделить всё
#include <linux/module.h>
#include <linux/version.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/moduleparam.h>
#include <net/arp.h>
#define ERR(...) printk(KERN_ERR "! "__VA_ARGS__)
#define LOG(...) printk(KERN_INFO "! "__VA_ARGS__)
static char* link = "eth0";
module_param(link, charp, 0);
static char* ifname = "virt";
module_param(ifname, charp, 0);
static struct net_device *child = NULL;
struct priv {
struct net_device_stats stats;
struct net_device *parent;
};
static rx_handler_result_t handle_frame(struct sk_buff **pskb) {
struct sk_buff *skb = *pskb;
if(child) {
struct priv *priv = netdev_priv(child);
priv->stats.rx_packets++;
priv->stats.rx_bytes += skb->len;
LOG("rx: injecting frame from %s to %s", skb->dev->name, child->name);
skb->dev = child;
/* netif_receive_skb(skb);
return RX_HANDLER_CONSUMED; */
return RX_HANDLER_ANOTHER;
}
return RX_HANDLER_PASS;
}
static int open(struct net_device *dev) {
netif_start_queue(dev);
LOG("%s: device opened", dev->name);
return 0;
}
static int stop(struct net_device *dev) {
netif_stop_queue(dev);
LOG("%s: device closed", dev->name);
return 0;
}
static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev) {
struct priv *priv = netdev_priv(dev);
priv->stats.tx_packets++;
priv->stats.tx_bytes += skb->len;
if(priv->parent) {
skb->dev = priv->parent;
skb->priority = 1;
dev_queue_xmit(skb);
LOG("tx: injecting frame from %s to %s", dev->name, skb->dev->name);
return 0;
}
return NETDEV_TX_OK;
}
static struct net_device_stats *get_stats(struct net_device *dev) {
return &((struct priv*)netdev_priv(dev))->stats;
}
static struct net_device_ops crypto_net_device_ops = {
.ndo_open = open,
.ndo_stop = stop,
.ndo_get_stats = get_stats,
.ndo_start_xmit = start_xmit,
};
// #define MAX_ADDR_LEN 32 <netdev.h>
// #define ETH_ALEN 6 /* Octets in one ethernet addr */ <if_ether.h>
static void setup(struct net_device *dev) {
int j;
ether_setup(dev);
memset(netdev_priv(dev), 0, sizeof(struct priv));
dev->netdev_ops = &crypto_net_device_ops;
for(j = 0; j < ETH_ALEN; ++j) // fill in the MAC address with a phoney
dev->dev_addr[j]= (char)j;
}
int __init init(void) {
int err = 0;
struct priv *priv;
char ifstr[40];
sprintf(ifstr, "%s%s", ifname, "%d");
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0))
child = alloc_netdev(sizeof(struct priv), ifstr, setup);
#else
child = alloc_netdev(sizeof(struct priv), ifstr, NET_NAME_UNKNOWN, setup);
#endif
if(child == NULL) {
ERR("%s: allocate error", THIS_MODULE->name); return -ENOMEM;
}
priv = netdev_priv(child);
priv->parent = __dev_get_by_name(&init_net, link); // parent interface
if(!priv->parent) {
ERR("%s: no such link: %s", THIS_MODULE->name, link);
err = -ENODEV; goto err;
}
if(priv->parent->type != ARPHRD_ETHER && priv->parent->type != ARPHRD_LOOPBACK) {
ERR("%s: illegal net type", THIS_MODULE->name);
err = -EINVAL; goto err;
}
/* also, and clone its IP, MAC and other information */
memcpy(child->dev_addr, priv->parent->dev_addr, ETH_ALEN);
memcpy(child->broadcast, priv->parent->broadcast, ETH_ALEN);
if((err = dev_alloc_name(child, child->name))) {
ERR("%s: allocate name, error %i", THIS_MODULE->name, err);
err = -EIO; goto err;
}
register_netdev(child);
rtnl_lock();
netdev_rx_handler_register(priv->parent, &handle_frame, NULL);
rtnl_unlock();
LOG("module %s loaded", THIS_MODULE->name);
LOG("%s: create link %s", THIS_MODULE->name, child->name);
LOG("%s: registered rx handler for %s", THIS_MODULE->name, priv->parent->name);
return 0;
err:
free_netdev(child);
return err;
}
void __exit virt_exit(void) {
struct priv *priv = netdev_priv(child);
if(priv->parent) {
rtnl_lock();
netdev_rx_handler_unregister(priv->parent);
rtnl_unlock();
LOG("unregister rx handler for %s\n", priv->parent->name);
}
unregister_netdev(child);
free_netdev(child);
LOG("module %s unloaded", THIS_MODULE->name);
}
module_init(init);
module_exit(virt_exit);
MODULE_AUTHOR("Oleg Tsiliuric");
MODULE_AUTHOR("Nikita Dorokhin");
MODULE_LICENSE("GPL v2");
MODULE_VERSION("3.2");
Функционально это: вы можете
навесить такой интерфейс на любой существующий, сделав ему любые сетевые присвоения, и работать через него
вместо родительского!
Для сетевых экспериментов -
это сила!