別の仮想むンタヌフェむス

前のメモで、远加の仮想ネットワヌクむンタヌフェむスを䜜成するためのLinuxカヌネルモゞュヌルのコヌドスケッチを瀺したした。 これは実際のプロゞェクトからの単玔化された断片であり、倱敗や苊情なしに数幎間機胜したため、さらなる改善、修正、開発のテンプレヌトずしお圹立぀可胜性がありたす。



しかし、この実装ぞのアプロヌチは、第䞀に、それだけではなく、第二に、状況によっおは受け入れられない堎合がありたすたずえば、netdev_rx_handler_registerがただ呌び出されおいない2.6.36よりも若いカヌネルを備えた組み蟌みシステム。 以䞋では、同じ機胜を備えた代替案を怜蚎したすが、TCP / IPネットワヌクスタックのたったく異なる局に実装したす。



ネットワヌク局プロトコル



繰り返しないように、TCP / IPネットワヌクスタックのレベルレむダヌはOSI / ISOオヌプン゜ヌスシステム盞互䜜甚モデルの7぀のレベルたたは、より正盎に蚀うず、孊界の䞭心に近いOSIモデルに明確に察応しおいないず曞かれおいたす䞍適切に進化しおいるTCP / IPネットワヌク。 前述の実装の仮想むンタヌフェむスの䜜成は、 むンタヌフェむスレベルL2、レベル2-OSIチャネルレベルにほが察応で実行されたした。 珟圚の実装では、 ネットワヌク局L3の機胜を䜿甚しおいたす。



ネットワヌクレベルのツヌルに関しお、珟圚のタスクに必芁なものよりもわずかに広いボリュヌムで、さらなる拡匵の可胜性のために、䞀定の最小倀を考慮するこずが適切ず思われたす。 ネットワヌクレベルでは、ネットワヌクプロトコルのスタックTCP / IPだけでなく、他のすべおのプロトコルファミリがここでサポヌトされおいたすが、今日ではほずんど関係がないようです、IP / IPv4 / IPv6、IPX、IGMP、RIPなどのプロトコルの凊理 、 OSPF、ARP、たたは元のナヌザヌプロトコルの远加 。 ネットワヌクレベルのハンドラヌをむンストヌルするには、ネットワヌクレベルのAPI<linux / netdevice.h>が提䟛されたす。

struct packet_type { __be16 type; /* This is really htons(ether_type). */ struct net_device *dev; /* NULL is wildcarded here */ int (*func) (struct sk_buff*, struct net_device*, struct packet_type*, struct net_device*); ... struct list_head list; }; extern void dev_add_pack( struct packet_type *pt ); extern void dev_remove_pack( struct packet_type *pt );
      
      





実際、カヌネルのプロトコルモゞュヌルでは、むンタヌフェむスの入力ストリヌムから゜ケットバッファヌが通過するフィルタヌを远加する必芁がありたす前の実装で瀺したように、出力ストリヌムはより簡単に実装されたす。 dev_add_pack関数は、func関数によっお実装される、指定されたタむプのパッケヌゞに別の新しいハンドラヌを远加したす。 この関数は、既存のハンドラヌLinuxネットワヌクシステムのデフォルトハンドラヌを含むを远加したすが、眮き換えたせん 。 凊理のために、この関数は、struct packet_type構造䜓に芏定された基準を満たす゜ケットバッファヌを遞択取埗したすプロトコルタむプおよびネットワヌクむンタヌフェむスdevのタむプに応じお。



泚同じ方法フィルタヌ機胜の蚭定で、ネットワヌクスタックのより高いトランスポヌトレベルで新しいプロトコルが远加されたすたずえば、UDP、TCP、SCTPプロトコルが凊理されたす。 すべおのより高いレベルOSIモデルのレベルずほが同じは、カヌネルでは衚珟されず、BSD゜ケットプログラミング技術によっおナヌザヌ空間で提䟛されたす。 しかし、これらの高レベルの詳现はすべお本文では考慮されなくなりたす。



新しいプロトコル独自仕様を远加する堎合は、そのタむプを再定矩する必芁がありたす。

 #define PROTO_ID 0x1234 static struct packet_type test_proto = { __constant_htons( PROT_ID ), ... }
      
      





問題は、暙準のIPスタックがそのようなプロトコルを認識しおいないこずであり、すべおの凊理を凊理する必芁がありたす。 しかし、私たちの目暙は、䞀郚のパケットの凊理を再定矩するこずだけです。そのため、定数ETH_P_ALLを䜿甚し、すべおのプロトコルがフィルタヌを通過するこずを瀺したすdevフィヌルドがNULLの堎合、すべおのネットワヌクむンタヌフェむスを通過する必芁がありたす。



比范ず具䜓化のために、倚数のプロトコル識別子むヌサネットプロトコルIDが<linux / if_ether.h>にありたす。以䞋に䟋を瀺したす。

 #define ETH_P_LOOP 0x0060 /* Ethernet Loopback packet */ #define ETH_P_IP 0x0800 /* Internet Protocol packet */ #define ETH_P_ARP 0x0806 /* Address Resolution packet */ #define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ #define ETH_P_ALL 0x0003 /* Every packet (be careful!!!) */ ...
      
      





この堎合、タむプフィヌルドはプログラムコヌドの抜象的な数倀ではありたせん。バむナリ圢匏のこの倀は、配信環境に物理的に送信されるフレヌムのむヌサネットヘッダヌに入力されたす。

 struct ethhdr { unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ unsigned char h_source[ETH_ALEN]; /* source ether addr */ __be16 h_proto; /* packet type ID field */ } __attribute__((packed));
      
      





モゞュヌルのstruct packet_type構造䜓に入力するずき、コヌドに同じ説明が必芁です。



フィルタヌ関数自䜓funcフィヌルドは、おそらく最も簡単な圢匏ではただ蚘述しおいたせんが、次のようなものです。

 int test_pack_rcv( struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *odev ) { LOG( "packet received with length: %u\n", skb->len ); kfree_skb( skb ); return skb->len; };
      
      





ここでは、䞻にkfree_skbの呌び出しが必須であるため、関数が瀺されおいたす。 送信チャネルのdev_kfree_skbの意味で䞀芋近いずは異なり、゜ケットバッファを砎壊せず 、その䜿甚カりンタusersフィヌルドを枛少させるだけです。 远加の各プロトコルフィルタヌがdev_add_packを呌び出しお蚭定されるず、この゜ケットバッファヌフィヌルドがむンクリメントされたす。 耇数のネットワヌクレベルのフィルタヌを同じもの、たたは耇数のロヌド可胜なモゞュヌルにむンストヌルでき、むンストヌルの逆の順序で機胜したすが、それぞれがkfree_skbを実行する必芁がありたす。 そうしないず、ネットワヌクスタックでゆっくりですが安定したメモリリヌクが発生するため、システムクラッシュのような結果は、数時間の連続操䜜埌にのみ怜出されたす。



これは非垞に興味深い堎所であり、明らかではありたせん。そのため、kfree_skbfile net / core / skbuff.cの実装の゜ヌスコヌドを泚意散挫にしお芋るのが理にかなっおいたす。

 void kfree_skb(struct sk_buff *skb) { if (unlikely(!skb)) return; if (likely(atomic_read(&skb->users) == 1)) smp_rmb(); else if (likely(!atomic_dec_and_test(&skb->users))) return; trace_kfree_skb(skb, __builtin_return_address(0)); __kfree_skb(skb); }
      
      





kfree_skbを呌び出すず、skb-> users == 1の堎合にのみ実際に゜ケットバッファヌが解攟され、他のすべおの倀では、skb-> users䜿甚量カりンタヌだけが枛少したす。



これで、仮想むンタヌフェむスの操䜜を敎理するのに十分な詳现が埗られたしたが、今回はIPスタックのネットワヌク局を䜿甚しおいたす。



仮想むンタヌフェヌスモゞュヌル



以前のように進みたすモゞュヌルの2぀のバヌゞョンを䜜成したす-ネットワヌクむンタヌフェヌスvirt0 が芪ネットワヌクむンタヌフェヌスに眮き換わる virtl.koの簡易バヌゞョンず、ネットワヌクプロトコルフレヌムARPおよびIP4を分析し、そのトラフィックのみに圱響するvirt.koのフルバヌゞョンこれはそのむンタヌフェヌスに関連しおいたす。 違いは、簡略化されたモゞュヌルのロヌド䞭、芪むンタヌフェヌスが䞀時的に動䜜を停止するこずですvirtl.koモゞュヌルがアンロヌドされるたで。フルバヌゞョンをロヌドする堎合、䞡方のむンタヌフェヌスが䞊行しお独立しお動䜜できたす。 完党なモゞュヌルのコヌドは明らかに面倒ですが、原則を理解するために䜕も远加したせん。 次に、原則を瀺す簡略版を詳现に怜蚎したすが、埌になっおから完党版に最䜎限觊れたすそのコヌドずテストレポヌトはサンプルアヌカむブに蚘茉されおいたす。

これはかなり長いコヌドです
 #include <linux/module.h> #include <linux/version.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/inetdevice.h> #include <linux/moduleparam.h> #include <net/arp.h> #include <linux/ip.h> #define ERR(...) printk( KERN_ERR "! "__VA_ARGS__ ) #define LOG(...) printk( KERN_INFO "! "__VA_ARGS__ ) #define DBG(...) if( debug != 0 ) printk( KERN_INFO "! "__VA_ARGS__ ) static char* link = "eth0"; module_param( link, charp, 0 ); static char* ifname = "virt"; module_param( ifname, charp, 0 ); static int debug = 0; module_param( debug, int, 0 ); static struct net_device *child = NULL; static struct net_device_stats stats; //     static u32 child_ip; struct priv { struct net_device *parent; }; static char* strIP( u32 addr ) { //  IP    static char saddr[ MAX_ADDR_LEN ]; sprintf( saddr, "%d.%d.%d.%d", ( addr ) & 0xFF, ( addr >> 8 ) & 0xFF, ( addr >> 16 ) & 0xFF, ( addr >> 24 ) & 0xFF ); return saddr; } static int open( struct net_device *dev ) { struct in_device *in_dev = dev->ip_ptr; struct in_ifaddr *ifa = in_dev->ifa_list; /* IP ifaddr chain */ LOG( "%s: device opened", dev->name ); child_ip = ifa->ifa_address; netif_start_queue( dev ); if( debug != 0 ) { char sdebg[ 40 ] = ""; sprintf( sdebg, "%s:", strIP( ifa->ifa_address ) ); strcat( sdebg, strIP( ifa->ifa_mask ) ); DBG( "%s: %s", dev->name, sdebg ); } return 0; } static int stop( struct net_device *dev ) { LOG( "%s: device closed", dev->name ); netif_stop_queue( dev ); return 0; } static struct net_device_stats *get_stats( struct net_device *dev ) { return &stats; } //   static netdev_tx_t start_xmit( struct sk_buff *skb, struct net_device *dev ) { struct priv *priv = netdev_priv( dev ); stats.tx_packets++; stats.tx_bytes += skb->len; skb->dev = priv->parent; //    ()  skb->priority = 1; dev_queue_xmit( skb ); DBG( "tx: injecting frame from %s to %s with length: %u", dev->name, skb->dev->name, skb->len ); return 0; return NETDEV_TX_OK; } static struct net_device_ops net_device_ops = { .ndo_open = open, .ndo_stop = stop, .ndo_get_stats = get_stats, .ndo_start_xmit = start_xmit, }; //   int pack_parent( struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *odev ) { skb->dev = child; //      stats.rx_packets++; stats.rx_bytes += skb->len; DBG( "tx: injecting frame from %s to %s with length: %u", dev->name, skb->dev->name, skb->len ); kfree_skb( skb ); return skb->len; }; static struct packet_type proto_parent = { __constant_htons( ETH_P_ALL ), //   : ETH_P_ARP & ETH_P_IP NULL, pack_parent, (void*)1, NULL }; int __init init( void ) { void setup( struct net_device *dev ) { //   ( GCC) int j; ether_setup( dev ); memset( netdev_priv( dev ), 0, sizeof( struct priv ) ); dev->netdev_ops = &net_device_ops; for( j = 0; j < ETH_ALEN; ++j ) //  MAC   dev->dev_addr[ j ] = (char)j; } 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 ); //   if( !priv->parent ) { ERR( "%s: no such net: %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; } 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 ); //    proto_parent.dev = priv->parent; dev_add_pack( &proto_parent ); //      LOG( "module %s loaded", THIS_MODULE->name ); LOG( "%s: create link %s", THIS_MODULE->name, child->name ); return 0; err: free_netdev( child ); return err; } void __exit virt_exit( void ) { struct priv *priv= netdev_priv( child ); dev_remove_pack( &proto_parent ); //    unregister_netdev( child ); dev_put( priv->parent ); free_netdev( child ); LOG( "module %s unloaded", THIS_MODULE->name ); LOG( "=============================================" ); } module_init( init ); module_exit( virt_exit ); MODULE_AUTHOR( "Oleg Tsiliuric" ); MODULE_LICENSE( "GPL v2" ); MODULE_VERSION( "3.7" );
      
      







すべおが非垞に透明です



仕組みは次のずおりです。





機䌚の拡倧



簡朔に、簡単に蚀えば、独自のトラフィックでのみ動䜜し、芪むンタヌフェむスの動䜜を䞭断しない本栌的な仮想むンタヌフェむスを䜜成する方法に぀いおモゞュヌルのフルバヌゞョンがアヌカむブで行うこず。 これを行うには、以䞋を行う必芁がありたす。





このようなフルりェむトモゞュヌルを䜿甚するず、たずえば、異なるむンタヌフェむス䞊で異なるIPを䜿甚しお2぀の䞊列SSHセッションを開くこずができ、実際には単䞀の共通物理むンタヌフェむスを䞊列に䜿甚したす。

 $ ssh olej@192.168.50.17 olej@192.168.50.17's password: Last login: Mon Jul 16 15:52:16 2012 from 192.168.1.9 ... $ ssh olej@192.168.56.101 olej@192.168.56.101's password: Last login: Mon Jul 16 17:29:57 2012 from 192.168.50.1 ... $ who olej tty1 2012-07-16 09:29 (:0) olej pts/0 2012-07-16 09:33 (:0.0) ... olej pts/6 2012-07-16 17:29 (192.168.50.1) olej pts/7 2012-07-16 17:31 (192.168.56.1)
      
      







瀺されおいる最埌のコマンド誰は、SSHセッションで、぀たり2぀の異なるサブネットからの2 ぀の独立した接続が固定されおいる出力の最埌の2行、実際には1぀のホストを衚しおいたすが、その芳点からは、SSHセッションで既に実行されおいたすさたざたなネットワヌクむンタヌフェむス。



さらなる掗緎



サンプルモゞュヌルの準備ずデバッグでは、詳现を明確にするために、このかなり最近の本が積極的に䜿甚されたした。RamiRosen「Linux Kernel NetworkingImplementation and Theory」、Apress、650ペヌゞ、2014幎、ISBN-13978-1-4302 -6196-4。





著者は、本が発売される前に無料でダりンロヌドできるように芪切に提䟛したした2013-12-22。 このペヌゞからダりンロヌドできたす 。



この蚘事で説明したような問題に興味がある人は、自分のネットワヌクむンタヌフェむスを䜿甚する技術をさらに発展させるために、この出版物で倚くのアむデアを芋぀けるこずができたす。



ここかここで実隓ずさらなる開発のためにテキストで蚀及されたコヌドのアヌカむブを取るこずができたす 。



All Articles