生活随笔
收集整理的這篇文章主要介紹了
网络设备驱动程序
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
1.1? 網絡設備的特殊性
網絡設備作為Linux的三類設備之一,有非常特殊的地方。每一個字符設備或塊設備在文件系統中都有一個相應的特殊設備文件來表示該設備,如/dev/hda1、/dev/sda1、/dev/tty1等。網絡設備與它們不同,所有網絡設備都抽象為一個接口,這個接口提供了對所有網絡設備的操作集合。網絡接口不存在于Linux的文件系統中,在/dev目錄下沒有對應的設備名,但每個網絡設備有自己的設備名稱,從設備名稱可以看出設備類型,如lo表示回環設備,eth表示以太網設備。同類型的多個設備從0向上編號,如以太網設備的編號為 eth0、eth1…ethn等。使用ifconfig命令可以查看當前活動的網卡信息。
[root@/home]ifconfig ?eth0??????Link?encap:Ethernet??HWaddr?00:0C:29:10:26:00?? ???????????inet?addr:192.168.1.101??Bcast:192.168.1.255??Mask:255.255.255.0 ???????????inet6?addr:?fe80::20c:29ff:fe10:2600/64?Scope:Link ???????????UP?BROADCAST?RUNNING?MULTICAST??MTU:1500??Metric:1 ???????????RX?packets:14106?errors:0?dropped:0?overruns:0?frame:0 ???????????TX?packets:21977?errors:0?dropped:0?overruns:0?carrier:0 ???????????collisions:0?txqueuelen:1000? ???????????RX?bytes:1416177?(1.3?MiB)??TX?bytes:22373971?(21.3?MiB) ???????????Interrupt:16?Base?address:0x2024? ?lo????????Link?encap:Local?Loopback?? ???????????inet?addr:127.0.0.1??Mask:255.0.0.0 ???????????inet6?addr:?::1/128?Scope:Host ???????????UP?LOOPBACK?RUNNING??MTU:16436??Metric:1 ???????????RX?packets:3273?errors:0?dropped:0?overruns:0?frame:0 ???????????TX?packets:3273?errors:0?dropped:0?overruns:0?carrier:0 ???????????collisions:0?txqueuelen:0? ???????????RX?bytes:6961876?(6.6?MiB)??TX?bytes:6961876?(6.6?MiB) ?
網絡應用程序通過Socket套接字接口、網絡協議層、網絡驅動程序訪問網絡設備。Linux的網絡系統主要基于BSD UNIX的socket機制。在網絡子系統和驅動程序之間定義有專門的數據結構(sk_buff)進行數據的傳遞。Linux網絡子系統為系統提供協議層支持、數據緩沖和流量控制等。
1.2? sk_buff結構
sk_buff結構是整個Linux內核網絡子系統中最核心的數據結構。Linux網絡各層之間的數據傳送都要通過sk_buff結構。sk_buff結構定義如下:
struct?sk_buff?{ ?????struct?sk_buff??????*next;?//雙向鏈表指針 ?????struct?sk_buff??????*prev; ?????struct?sock?????????*sk;?//?擁有這個sk_buff的sock結構 ?????ktime_t?????????tstamp;?//到達或發送的時間戳 ?????struct?net_devic????e???*dev;//關聯的網絡設備 ?????????struct??dst_entry???*dst;?//?路由子系統中使用 ?????struct??sec_path????????*sp;?//?IPSec協議用于跟蹤傳輸的信息 ?????char????????????cb[48];//各層私有數據 ?????unsigned?int????????len,?//當前協議數據包的長度 ?????????????????data_len;?//分片中數據的長度 ?????__u16???mac_len,//mac頭的長度 ?????????????hdr_len; ?????union?{ ?????????__wsum??????csum; ?????????struct?{ ?????????????__u16???csum_start; ?????????????__u16???csum_offset; ?????????}; ?????}; ?????__u32???????priority; ?????__u8????????local_df:1, ?????????????????cloned:1, ?????????????????ip_summed:2, ?????????????????nohdr:1, ?????????????????nfctinfo:3; ?????__u8????????pkt_type:3,//?幀的類型 ?????????????????fclone:2, ?????????????????ipvs_property:1, ?????????????????nf_trace:1; ?????__be16??????protocol;//協議,包括IP、IPV6、ARP等 ????//?在緩沖區釋放時完成某些動作的函數 ?????void????????????(*destructor)(struct?sk_buff?*skb); ?#if?defined(CONFIG_NF_CONNTRACK)?||?defined(CONFIG_NF_CONNTRACK_MODULE) ?????struct?nf_conntrack?*nfct; ?????struct?sk_buff??????*nfct_reasm; ?????#endif ?????#ifdef?CONFIG_BRIDGE_NETFILTER?//netfilter防火墻使用 ?????struct?nf_bridge_info???*nf_bridge; ?????#endif ?????int?????????????iif; ?????#ifdef?CONFIG_NETDEVICES_MULTIQUEUE ?????__u16???????????queue_mapping; ?????#endif ?????#ifdef?CONFIG_NET_SCHED ?????__u16???????????tc_index; ?????#ifdef?CONFIG_NET_CLS_ACT ?????__u16???????????tc_verd;????/*流量控制機制*/ ?????#endif ?????#endif ?????#ifdef?CONFIG_NET_DMA ?????dma_cookie_t????????dma_cookie; ?????#endif ?????#ifdef?CONFIG_NETWORK_SECMARK ?????__u32???????????secmark; ?????#endif ?????__u32???????????mark; ?????sk_buff_data_t??????transport_header;//傳輸層頭部 ?????sk_buff_data_t??????network_header;//網絡層頭部 ?????sk_buff_data_t??????mac_header;//MAC層頭部 ?????sk_buff_data_t??????tail; ?????sk_buff_data_t??????end; ?????unsigned?char???????*head,*data;//數據區 ?????unsigned?int????????truesize;//真實大小 ?????atomic_t????????users;?//?引用計數 ?}; ?
1.3? Linux網絡設備驅動程序架構
網絡設備被抽象為統一的接口供系統訪問,應用層對各種網絡設備的訪問都采用統一的形式,也就是套接字形式,它具有硬件無關性。
網絡設備驅動程序最重要的結構是struct net_device。struct net_device結構保存一個網絡接口的重要信息,是網絡驅動程序的核心。struct net_device結構非常龐大,下面介紹該結構中幾個最重要的成員:
struct?net_device ?{ ?????char?????name[IFNAMSIZ];?//設備名 ?????struct?hlist_node???name_hlist; ?????unsigned?long???????mem_end;?//共享內存的尾地址 ?????unsigned?long???????mem_start;?//共享內存的首地址 ?????unsigned?long???????base_addr;?//設備的I/O地址 ?????unsigned?int????????irq;?//設備的中斷號 ?????unsigned?char???????if_port; ?????unsigned?char???????dma;????//設備用的DMA通道 ?????unsigned?long???????state; ?????struct?list_head????dev_list; ?????int?(*init)(struct?net_device?*dev);//設備初始化函數 ?????unsigned?long???????features;//設備特性 ?????struct?net_device???*next_sched; ?????int?????????ifindex; ?????int?????????iflink; ?????struct?net_device_stats*?(*get_stats)(struct?net_device?*dev);//獲取統計數據 ?????struct?net_device_stats?stats;//統計數據 ?????… ?????int?(*hard_start_xmit)?(struct?sk_buff?*skb,struct?net_device?*dev);//數據發送 ?????const?struct?ethtool_ops?*ethtool_ops; ?????const?struct?header_ops?*header_ops; ?????int??(*open)(struct?net_device?*dev);?//打開網絡接口 ?????int??(*stop)(struct?net_device?*dev);//關閉網絡接口 ?????void?????(*change_rx_flags)(struct?net_device?*dev,int?flags); ?????void?????(*set_rx_mode)(struct?net_device?*dev); ?????//配置多播地址鏈表 ?????void?????(*set_multicast_list)(struct?net_device?*dev); ?????//配置MAC地址 ?????int??(*set_mac_address)(struct?net_device?*dev,void?*addr); ?????int??(*validate_addr)(struct?net_device?*dev);?//地址驗證 ?????//執行特殊的ioctl命令 ?????int??(*do_ioctl)(struct?net_device?*dev,struct?ifreq?*ifr,?int?cmd); ?????//改變接口配置 ?????int??(*set_config)(struct?net_device?*dev,struct?ifmap?*map); ?????//當MTU改變時驅動程序要做一些特殊的事情,就應該寫這個函數 ?????int??(*change_mtu)(struct?net_device?*dev,?int?new_mtu); ?????void?????(*tx_timeout)?(struct?net_device?*dev);?//發送超時處理 ?????void?????(*vlan_rx_register)(struct?net_device?*dev,struct?vlan_group?*grp); ?????void?????(*vlan_rx_add_vid)(struct?net_device?*dev,unsigned?short?vid); ?????void?????(*vlan_rx_kill_vid)(struct?net_device?*dev,unsigned?short?vid); ?????int??(*neigh_setup)(struct?net_device?*dev,?struct?neigh_parms?*); ?????… ?}; ?
網絡設備注冊和注銷函數原型如下:
int?register_netdev(struct?net_device?*dev); ?void?unregister_netdev(struct?net_device?*dev);?
網絡設備驅動程序與網絡子系統直接交互。兩者交互的基本單位是sk_buff結構。網絡系統下發數據要通過dev_queue_xmit函數,而dev_queue_xmit函數會調用網絡設備的hard_start_xmit接口。網絡設備收到數據后會產生一個中斷,在中斷處理程序中驅動程序申請一塊sk_buff,從硬件讀出數據放置到申請好的緩沖區里,接下來填充sk_buff中的一些成員,最后驅動程序調用netif_rx把數據傳送給協議層,netif_rx將數據放入處理隊列然后返回,真正的處理是在中斷返回以后,這樣可以減少中斷時間。
int?dev_queue_xmit(struct?sk_buff?*skb); ?int?(*hard_start_xmit)?(struct?sk_buff?*skb,struct?net_device?*dev); ?int?netif_rx(struct?sk_buff?*skb); ?
網絡設備驅動程序的主要功能是實現struct net_device中的函數接口。
(1)open接口
int??(*open)(struct?net_device?*dev);?
open接口在網絡設備被激活的時候被調用。它主要完成資源和中斷的申請、DMA的注冊等工作。使用ifconfig命令可以激活網絡設備。
(2)hard_start_xmit接口
int?(*hard_start_xmit)?(struct?sk_buff?*skb,struct?net_device?*dev);?
hard_start_xmit接口用來將網絡子系統發來的數據通過網卡發送到物理網絡。struct net_device中沒有讀數據接口,讀數據一般在網卡中斷中處理。
(3)get_stats接口
struct?net_device_stats*?(*get_stats)(struct?net_device?*dev);?
get_stats函數返回一個net_device_stats結構,該結構保存了所管理的網絡設備接口的詳細的流量與錯誤統計信息:
struct?net_device_stats ?{ ?????unsigned?long???rx_packets;//接收的總包數 ?????unsigned?long???tx_packets;//發送的總包數 ?????unsigned?long???rx_bytes;???//接收總字節數 ?????unsigned?long???tx_bytes;?//發送總字節數 ?????unsigned?long???rx_errors;??//收到的錯包數量 ?????unsigned?long???tx_errors;??//發送的錯包數量 ?????unsigned?long???rx_dropped;//丟棄的接收包數量 ?????unsigned?long???tx_dropped;?//丟棄的發送包數量 ?????unsigned?long???multicast;??//接收的多播包數 ?????unsigned?long???collisions; ?????/*詳細的接收錯誤*/ ?????unsigned?long???rx_length_errors;//長度錯誤 ?????unsigned?long???rx_over_errors;//環行緩沖溢出錯誤 ?????unsigned?long???rx_crc_errors;??//CRC校驗錯誤 ?????unsigned?long???rx_frame_errors;//幀對齊錯誤 ?????unsigned?long???rx_fifo_errors;?//接收緩沖溢出錯誤 ?????unsigned?long???rx_missed_errors;//接收者遺漏錯誤 ?????/*詳細的發送錯誤*/ ?????unsigned?long???tx_aborted_errors; ?????unsigned?long???tx_carrier_errors; ?????unsigned?long???tx_fifo_errors; ?????unsigned?long???tx_heartbeat_errors; ?????unsigned?long???tx_window_errors; ????? ?????unsigned?long???rx_compressed; ?????unsigned?long???tx_compressed; ?}; ?
(4)do_ioctl接口
int??(*do_ioctl)(struct?net_device?*dev,struct?ifreq?*ifr,?int?cmd);?
Linux有一些特定的socket ioctl,定義在sockios.h頭文件中。網絡子系統一般已經實現了這些ioctl命令,也有的命令需要在自定義的驅動程序中實現。常用的ioctl命令有:
#define?SIOCGIFMTU???????0x8921?????/*獲取MTU大小*/ ?#define?SIOCSIFMTU???????0x8922?????/*配置MTU大小*/ ?#define?SIOCSIFNAME??????0x8923?????/*配置接口名稱*/ ?#define?SIOCSIFHWADDR??0x8924???????/*配置硬件地址*/ ?#define?SIOCGIFENCAP?????0x8925?????/*獲取封裝屬性*/ ?#define?SIOCSIFENCAP?????0x8926?????/*配置封裝屬性*/ ?#define?SIOCGIFHWADDR????0x8927?????/*獲取硬件地址*/ ?#define?SIOCADDMULTI?????0x8931?????/*增加廣播地址*/ ?#define?SIOCDELMULTI?????0x8932?????????/*刪除廣播地址*/ ?#define?SIOCGIFBRDADDR?0x8919???????/*得到廣播地址*/ ?#define?SIOCSIFBRDADDR???0x891a?????/*配置廣播地址*/ ?
例1.11? 使用SIOCGIFHWADDR獲取MAC地址
本例介紹如何使用SIOCGIFHWADDR獲取網卡MAC地址。參考代碼如下:
int?fd;? ?struct?ifreq?ifr; ?fd?=?socket(AF_INET,?SOCK_DGRAM,?0);? ?ifr.ifr_addr.sa_family?=?AF_INET;? ?strncpy(ifr.ifr_name,?"eth0",?IFNAMSIZ-1);? ?ioctl(fd,?SIOCGIFHWADDR,?&ifr);? ?close(fd);? ?printf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",? ?(unsigned?char)ifr.ifr_hwaddr.sa_data[0],? ?(unsigned?char)ifr.ifr_hwaddr.sa_data[1],? ?(unsigned?char)ifr.ifr_hwaddr.sa_data[2],? ?(unsigned?char)ifr.ifr_hwaddr.sa_data[3],? ?(unsigned?char)ifr.ifr_hwaddr.sa_data[4],? ?(unsigned?char)ifr.ifr_hwaddr.sa_data[5]); ?
do_ioctl一般用來實現驅動程序私有的ioctl命令,命令的類型在SIOCDEVPRIVATE和SIOCDEVPRIVATE+15之間。
(5)set_multicast_list接口
void?????(*set_multicast_list)(struct?net_device?*dev);?
set_multicast_list接口在設備地址更改或dev->flags 被修改時調用。在以太網的默認初始化函數中,dev->flags被配置為IFF_BROADCAST|IFF_MULTICAST,表示以太網卡是可廣播的,并且是能夠進行組播發送的。對dev->flags配置或清除IFF_PROMISC標志時,會調用set_multicast_list函數通知板卡上的硬件過濾器。
(6)set_mac_address接口
int?set_mac_address(struct?net_device?*dev,?void?*p)?
該接口用來配置網絡設備的MAC地址,MAC地址就存放在p指針中。
(7)stop接口
int??(*stop)(struct?net_device?*dev);?
stop接口在網卡狀態由up轉為down時被調用,一般用來釋放資源。
例1.12? 虛擬網絡設備驅動程序實例
下面的例子是一個虛擬網卡驅動程序,代碼見光盤\src\1drivermodel\1-11net。核心代碼如下所示:
static?char??netbuffer[100]; ?struct?net_device?*simnetdevs; ?void?simnetrx(struct?net_device?*dev,?int?len,?unsigned?char?*buf) ?{ ?????struct?sk_buff?*skb; ?????struct?simnetpriv?*priv?=?(struct?simnetpriv?*)?dev->priv; ?????skb?=?dev_alloc_skb(len+2); ?????if?(!skb)?{ ?????????printk("simnetrx?can?not?allocate?more?memory?to?store?the?packet.?drop?the?packet\n"); ?????????priv->stats.rx_dropped++; ?????????return; ?????} ?????skb_reserve(skb,?2); ?????memcpy(skb_put(skb,?len),?buf,?len); ?????skb->devdev?=?dev; ?????skb->protocol?=?eth_type_trans(skb,?dev); ?????/*?不需要校驗?*/ ?????skb->ip_summed?=?CHECKSUM_UNNECESSARY;? ?????priv->stats.rx_packets++; ?????netif_rx(skb);//將數據送入內核網絡層 ?????return; ?} ?static?irqreturn_t?simnet_interrupt?(int?irq,?void?*dev_id) ?{ ?????struct?net_device?*dev; ?????dev?=?(struct?net_device?*)?dev_id; ?????//從硬件獲取數據 ?????simnetrx(dev,100,netbuffer); ?????return?IRQ_HANDLED; ?} ?int?simnetopen(struct?net_device?*dev) ?{ ?????int?ret=0; ?????printk("simnetopen\n"); ?????//申請中斷 ?????ret?=?request_irq(IRQ_NET_CHIP,?simnet_interrupt,?IRQF_SHARED,dev->name,?dev); ?????if?(ret)?return?ret; ?????printk("request_irq?ok\n"); ?????netif_start_queue(dev); ?????return?0; ?} ?int?simnetrelease(struct?net_device?*dev) ?{ ?????printk("simnetrelease\n"); ?????netif_stop_queue(dev);?????????? ?????return?0; ?} ?//虛擬硬件發送 ?void?simnethw_tx(char?*buf,?int?len,?struct?net_device?*dev) ?{ ?????struct?simnetpriv?*priv; ?????/*?長度檢查?*/ ?????if?(len?<?sizeof(struct?ethhdr)?+?sizeof(struct?iphdr)) ?????{ ?????????printk("Bad?packet!?It's?size?is?less?then?34!\n"); ?????????return; ?????} ?????/*?保存狀態*/ ?????priv?=?(struct?simnetpriv?*)?dev->priv; ?????priv->stats.tx_packets++; ?????priv->stats.rx_bytes?+=?len; ?????/*?釋放內存?*/ ?????dev_kfree_skb(priv->skb); ?} ?//發送數據包 ?int?simnettx(struct?sk_buff?*skb,?struct?net_device?*dev) ?{ ?????int?len; ?????char?*data; ?????struct?simnetpriv?*priv?=?(struct?simnetpriv?*)?dev->priv; ?????len?=?skb->len?<?ETH_ZLEN???ETH_ZLEN?:?skb->len; ?????data?=?skb->data; ?????/*添加時間戳?*/ ?????dev->trans_start?=?jiffies; ?????//記錄skb以便在simnethw_tx中釋放 ?????priv->skbskb?=?skb; ?????simnethw_tx(data,?len,?dev); ?????return?0;? ?} ?//處理發送超時 ?void?simnettx_timeout?(struct?net_device?*dev) ?{ ?????struct?simnetpriv?*priv?=?(struct?simnetpriv?*)?dev->priv; ?????priv->stats.tx_errors++; ?????netif_wake_queue(dev); ?????return; ?} ?//?ioctl控制 ?int?simnetioctl(struct?net_device?*dev,?struct?ifreq?*rq,?int?cmd) ?{ ?????return?0; ?} ?struct?net_device_stats?*simnetstats(struct?net_device?*dev) ?{ ?????struct?simnetpriv?*priv?=?(struct?simnetpriv?*)?dev->priv; ?????return?&priv->stats; ?} ?//變更mtu ?int?simnetchange_mtu(struct?net_device?*dev,?int?new_mtu) ?{ ?????unsigned?long?flags; ?????spinlock_t?*lock?=?&((struct?simnetpriv?*)?dev->priv)->lock; ?????if?(new_mtu?<?68) ?????????return?-EINVAL; ?????spin_lock_irqsave(lock,?flags); ?????dev->mtu?=?new_mtu; ?????spin_unlock_irqrestore(lock,?flags); ?????return?0;? ?} ?//設備初始化 ?void?simnetinit(struct?net_device?*dev) ?{ ?????struct?simnetpriv?*priv; ?????ether_setup(dev); ?????dev->open=?simnetopen; ?????dev->stop=?simnetrelease; ?????dev->hard_start_xmit?=?simnettx; ?????dev->do_ioctl=?simnetioctl; ?????dev->get_stats=?simnetstats; ?????dev->change_mtu?=?simnetchange_mtu;?? ?????dev->tx_timeout?=?simnettx_timeout; ?????//配置MAC地址,注意如果(0x01?&?addr[0])為真,則是multicast地址 ?????dev->dev_addr[0]?=?0x18; ?????dev->dev_addr[1]?=?0x02; ?????dev->dev_addr[2]?=?0x03; ?????dev->dev_addr[3]?=?0x04; ?????dev->dev_addr[4]?=?0x05; ?????dev->dev_addr[5]?=?0x06; ?????dev->flags|=?IFF_NOARP;//不支持ARP ?????dev->features|=?NETIF_F_NO_CSUM; ?????priv?=?netdev_priv(dev); ?????memset(priv,?0,?sizeof(struct?simnetpriv)); ?????spin_lock_init(&priv->lock); ?} ?//模塊卸載 ?void?simnetcleanup(void) ?{ ?????if?(simnetdevs)? ?????{ ?????????unregister_netdev(simnetdevs); ?????????free_netdev(simnetdevs); ?????} ?????return; ?} ?//模塊初始化 ?int?simnetinit_module(void) ?{ ?????int?result,ret?=?-ENOMEM; ?????//分配網絡設備 ?????simnetdevs=alloc_netdev(sizeof(struct?simnetpriv),?"eth%d", ?????????????simnetinit); ?????if?(simnetdevs?==?NULL) ?????????goto?out; ?????ret?=?-ENODEV; ?????//注冊網絡設備 ?????if?((result?=?register_netdev(simnetdevs))) ?????????printk("demo:?error?%i?registering?device?\"%s\"\n",result,?simnetdevs->name); ?????else ?????????ret?=?0; ?out: ?????if?(ret)? ?????????simnetcleanup(); ?????return?ret; ?} ?module_init(simnetinit_module); ?module_exit(simnetcleanup); ?
運行結果如下:
[root@urbetter?/home]#?insmod??demo.ko? ?[root@urbetter?/home]#?ifconfig?eth1?192.168.1.23 ?simnetopen ?request_irq?ok ?[root@urbetter?/home]#?ifconfig?eth1?up ?[root@urbetter?/home]#?ping?192.168.1.23 ?PING?192.168.1.23?(192.168.1.23):?56?data?bytes ?64?bytes?from?192.168.1.23:?seq=0?ttl=64?time=1.347?ms ?64?bytes?from?192.168.1.23:?seq=1?ttl=64?time=0.301?ms ?64?bytes?from?192.168.1.23:?seq=2?ttl=64?time=0.266?ms ?^C ?---?192.168.1.23?ping?statistics?--- ?3?packets?transmitted,?3?packets?received,?0%?packet?loss ?round-trip?min/avg/max?=?0.266/0.638/1.347?ms ?[root@urbetter?/home]#?ifconfig?eth1 ?eth1??????Link?encap:Ethernet??HWaddr?18:02:03:04:05:06?? ???????????inet?addr:192.168.1.23??Bcast:192.168.1.255??Mask:255.255.255.0 ???????????UP?BROADCAST?RUNNING?NOARP?MULTICAST??MTU:1500??Metric:1 ???????????RX?packets:0?errors:0?dropped:0?overruns:0?frame:0 ???????????TX?packets:0?errors:0?dropped:0?overruns:0?carrier:0 ???????????collisions:0?txqueuelen:1000? ???????????RX?bytes:0?(0.0?B)??TX?bytes:0?(0.0?B) ?[root@urbetter?/home]#?ifconfig?eth1?192.168.1.26 ?[root@urbetter?/home]#?ifconfig?eth1?down ?simnetrelease ?[root@urbetter?/home]#?ifconfig?eth1?192.168.1.26 ?simnetopen ?request_irq?ok ?
總結
以上是生活随笔為你收集整理的网络设备驱动程序的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。